﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using MathNet.Numerics.Distributions;
using MentalAlchemy.Atomics;
using MentalAlchemy.Molecules;
using MentalAlchemy.Molecules.MachineLearning;

namespace EvolutionaryAlgorithm
{
	public partial class Form1 : Form
	{
		protected const float DEFAULT_MIN_GENE_VALUE = -5f;
		protected const float DEFAULT_GENE_VALUE_RANGE = 10f;

		protected const string DEFAULT_LOG_FILENAME = "ea.log";

		public Form1()
		{
			InitializeComponent();

			// set problems combo.
			var funcs = ObjectiveFunctions.Functions();
			foreach (var func in funcs)
			{
				ProblemCombo.Items.Add(func);
			}
			ProblemCombo.SelectedIndex = 0;

			// set algorithms combo.
			var eas = EvolutionaryAlgorithms.Algorithms();
			foreach (var ea in eas)
			{
				AlgorithmCombo.Items.Add(ea);
			}
			AlgorithmCombo.SelectedIndex = 0;

			eaPropertiesControl1.Parameters.GenerationsNumber = 1000;
			eaPropertiesControl1.Parameters.PopulationSize = 100;
		}

		private void RunBtn_Click(object sender, EventArgs e)
		{
			var time = new Stopwatch();
			var eaName = (string)AlgorithmCombo.SelectedItem;

			// todo: implement analysis of multiple runs.
			var ea = EvolutionaryAlgorithms.GetAlgorithm(eaName);
			ea.FitnessFunction = ObjectiveFunctions.GetFunction((string)ProblemCombo.SelectedItem);

			var parameters = eaPropertiesControl1.Parameters;
			parameters.IndividualSize = (int)VarsNumeric.Value;
			parameters.GeneValueRange = DEFAULT_GENE_VALUE_RANGE;
			parameters.MinGeneValue = DEFAULT_MIN_GENE_VALUE;
			parameters.MRate = 1f/parameters.IndividualSize;
			FitnessComparator.MinimizeFitness = MinimizeCheck.Checked;

			var stats = new List<List<Stats>>();
			var runsCount = (int)RunsNumeric.Value;
			var times = new int[runsCount];
			for (int i = 0; i < runsCount; i++)
			{
				time.Reset();
				time.Start();
				ea.Run(parameters);
				time.Stop();
				stats.Add(ea.FitnessStats);
				times[i] = (int)time.ElapsedMilliseconds;
			}

			// get and display EA results.
			var avgStats = StructMath.Average(stats);
			var statsList = MatrixMath.CreateFromStatsList(avgStats);
			var statsStr = MatrixMath.ConvertToStringsList(statsList);
			dataTableControl1.DataRows = statsStr;
			dataTableControl1.Header = ea.FitnessStats[0].Header.Split('\t');

			var timeStats = VectorMath.CalculateStats(times);
			TimeBox.Text = timeStats.Mean + " msec";

			var logLines = GetLogLines(parameters, eaName, (string)ProblemCombo.SelectedItem, runsCount, avgStats, timeStats);
			File.WriteAllLines(GetLogFilename(), logLines.ToArray());
		}

		private void TestPcxBtn_Click(object sender, EventArgs e)
		{
			var pars = new List<float[]>();
			pars.Add(new[] { 0.8f, 0.8f });
			pars.Add(new[] { 0f, -0.5f });
			pars.Add(new[] { -0.4f, 0.15f });

			var res = new List<float[]>();
			for (int i = 0; i < 1000; ++i )
			{
				var offsp = EAElements.CrossPcx(pars, 2);
				res.AddRange(offsp);

				//var off = (float[])pars[0].Clone();
				//off[0] += (float)(0.01 * rnd.NextDouble());
				//off[1] += (float)(0.01 * rnd.NextDouble());
				//res.Add(off);
			}

			using (var writer = new StreamWriter("pcx.log"))
			{
				var strs = MatrixMath.ConvertToStringsList(pars);
				FileIO.WriteColumns(writer, strs, "parents");
				strs = MatrixMath.ConvertToStringsList(res);
				FileIO.WriteColumns(writer, strs, "offspring");
			}
		}

		protected string GetLogFilename ()
		{
			return (string)AlgorithmCombo.SelectedItem + "_" + (string)ProblemCombo.SelectedItem + VarsNumeric.Value + ".log";
		}

		protected static List<string> GetLogLines(EAParameters eaparams, string algName, string funcName, int runsCount, List<Stats> stats, Stats timeStats)
		{
			var res = eaparams.ToStrings();
			res.Add("");
			res.Add("Algorithm:\t" + algName);
			res.Add("Fitness Function:\t" + funcName);
			res.Add("Runs count:\t" + runsCount);
			res.Add("");
			res.Add("Time stats:");
			res.Add(timeStats.GetStatsString());
			res.Add("");
			res.AddRange(StructMath.ConvertToStringsList(stats, true));
			return res;
		}

		private void TestIdeaBtn_Click(object sender, EventArgs e)
		{
			return;
			// create IDEA instance.
			var idea = new AmalgamIDEA();
			idea.FitnessFunction = ObjectiveFunctions.GetFunction((string)ProblemCombo.SelectedItem);

			var parameters = eaPropertiesControl1.Parameters;
			parameters.IndividualSize = (int)VarsNumeric.Value;
			parameters.GeneValueRange = DEFAULT_GENE_VALUE_RANGE;
			parameters.MinGeneValue = DEFAULT_MIN_GENE_VALUE;
			parameters.MRate = 1f / parameters.IndividualSize;
			FitnessComparator.MinimizeFitness = MinimizeCheck.Checked;

			var time = new Stopwatch();
			time.Start();
			idea.Run(parameters);
			time.Stop();

			// get and display EA results.
			var statsList = MatrixMath.CreateFromStatsList(idea.FitnessStats);
			var statsStr = MatrixMath.ConvertToStringsList(statsList);
			dataTableControl1.DataRows = statsStr;
			dataTableControl1.Header = idea.FitnessStats[0].Header.Split('\t');

			TimeBox.Text = time.ElapsedMilliseconds + " msec";

			//var logLines = GetLogLines(parameters, "Amalgam IDEA", (string)ProblemCombo.SelectedItem, idea.FitnessStats);
			//File.WriteAllLines(DEFAULT_LOG_FILENAME, logLines.ToArray());
		}
	}
}
